/**
 * iFramer - The IFrame Manipulator
 *
 * http://code.google.com/p/iframer
 *
 * Copyright:
 *     2010 Jose Badeau
 *
 * License:
 *     http://www.gnu.org/licenses/gpl.html
 *
 * Authors:
 *     Jose Badeau, jose.badeau@gmail.com
 *
 * version: 1.1
 *
 * Issues:
 *     Most browsers do not properly display the 'html', 'body' or 'document' widths or heights when an iframe's content shrinks.
 *     This means that the resizeIFrames method can only resize if the content size increase but not if it decreases.
 *     This can be worked around by providing a resize hint.
 */
(function(jQuery) {

    // prevent javascript errors in non-firefox browsers
    if (typeof console == 'undefined' || typeof window.console.firebug == 'undefined') {
        var logFunctions = ['log', 'debug', 'info', 'warn', 'error'];
        window.console = {};
        for (var i in logFunctions) {
            window.console[logFunctions[i]] = function() {
            };
        }
    }

    // default settings
    var settings = {
        disableWidthResizing: false,
        disableHeightResizing: false,
        resizeHints: new Array(),
        debug: false
    };

    /**
     * Resizes the selected iframes to the size of their contained content.
     *
     * The following settings can be overridden or defined with the options array:
     *     disableWidthResizing {boolean} (false): enable or disable resizing of iframe widths
     *
     *     disableHeightResizing {boolean} (false): enables or disable resizing of iframe heights
     *
     *     resizeHints {object} ({}): an object of jquery selector strings that are used to help resize specific
     *                                iframes if the default algorithm is unsatisfactory. The format is:
     *                                    key {string}: the dom id of the iframe to resize
     *
     *                                    value {string}: the jquery selector string of a block element inside the iframe
     *                                                    that is used to calculate the iframe's size
     *
     *     debug {boolean} (false): enable debug logging (firefox only)
     *
     * @param customSettings {object} an object of custom settings
     * @return [object] a jQuery object
     */
    jQuery.fn.resizeIFrames = function(customSettings) {

        // merge default and custom settings
        settings = jQuery.extend(settings, customSettings);

        // iterate and resize each iframe
        return this.each(function() {

            var $iframe = jQuery(this);

            if (settings.debug) {
                console.debug('[%s]', $iframe.attr('id'));
            }

            var resizeHint = _getResizeHint($iframe, settings);

            // resize width
            if (!$iframe.data('resizingDisabled')) {
                _resizeWidth($iframe, resizeHint);
            }

            // resize height
            if (!$iframe.data('resizingDisabled')) {
                _resizeHeight($iframe, resizeHint);
            }

            // add blank line to help with log readability
            if (settings.debug) {
                console.debug(' ');
            }

        });

    };

    /**
     * Resizes the width of an iframe.
     *
     * @param $iframe {object} an iframe dom object
     * @param resizeHint {object} optional iframe resize hint
     */
    function _resizeWidth($iframe, resizeHint) {

        var iframeWidth = $iframe.width();
        var iframeContentWidth;

        try {

            if (resizeHint) {
                contentWidth = resizeHint.width;
            }
            else {
                if (document.all) {
                    contentWidth = $iframe.contents().find('body').attr('scrollWidth');
                } else {
                    contentWidth = $iframe.contents().find('html').attr('scrollWidth');
                }
            }

            if (settings.debug) {
                console.debug('iframe width: %i', iframeWidth);
                console.debug('content width: %i', contentWidth);
            }

            if (iframeWidth != contentWidth) {
                $iframe.width(contentWidth);
            }

        }
        catch(e) {
            if (settings.debug) {
                console.error('error updating width; skipping resizing');
                console.error('exception: %o', e);
            }
            $iframe.data('resizingDisabled', true);
        }
    }

    /**
     * Resizes the height of an iframe.
     *
     * @param $iframe {object} an iframe dom object
     * @param resizeHint {object} optional iframe resize hint
     */
    function _resizeHeight($iframe, resizeHint) {

        var iframeHeight = $iframe.height();
        var iframeContentHeight;

        try {
            if (resizeHint) {
                iframeContentHeight = resizeHint.height;
            }
            else {
                if (document.all) {
                    iframeContentHeight = $iframe.contents().find('body').attr('scrollHeight');
                } else {
                    iframeContentHeight = $iframe.contents().find('body').attr('scrollHeight');
                }
            }

            if (settings.debug) {
                console.debug('iframe height: %i', iframeHeight);
                console.debug('content height: %i', iframeContentHeight);
            }

            if (iframeHeight != iframeContentHeight) {
                $iframe.height(iframeContentHeight);
            }

        }
        catch(e) {
            if (settings.debug) {
                console.error('error updating height; skipping resizing');
                console.error('exception: %o', e);
            }
            $iframe.data('resizingDisabled', true);
        }
    }

    /**
     * Gets the resize hint (width and height) for an iframe;
     *
     * @param $iframe {object} an iframe dom object
     * @param settings {object} an array of custom settings
     * @return {object} the resize hint for an iframe; undefined if not found
     */
    function _getResizeHint($iframe, settings) {
        var resizeHint;
        var resizeHints = settings.resizeHints;
        var iframeId = $iframe.attr('id');
        var resizeHintSelector = resizeHints[iframeId];
        if (resizeHintSelector) {
            try {
                if (resizeHintSelector != null) {
                    var $domElement = $iframe.contents().find(resizeHintSelector);
                    if ($domElement.length < 1) {
                        if (settings.debug) {
                            console.warn('resize hint %s for iframe %s did not return a dom element', resizeHintSelector, iframeId);
                        }
                    }
                    else if ($domElement.length > 1) {
                        if (settings.debug) {
                            console.warn('resize hint %s for iframe %s returned %s elements', resizeHintSelector, iframeId, $domElement.length);
                        }
                    }
                    else {
                        resizeHint = {width: $domElement.width(), height: $domElement.height()};
                        if (settings.debug) {
                            console.debug('resize hint %s for iframe %s returned %o', resizeHintSelector, iframeId, resizeHint);
                        }
                    }
                }
            }
            catch(e) {
                if (settings.debug) {
                    console.error('error locating iframe %s resize hint, skipping resizing', iframeId);
                    console.error('exception %o', e);
                }
                $iframe.data('resizingDisabled', true);
            }
        }
        else {
            if (settings.debug) {
                console.info('no resize hint found for iframe %s', iframeId);
            }
        }
        return resizeHint;
    }

})(jQuery);
